// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

namespace SnapObjects.Data
{
    internal class SqlGroupBuilder : SqlBuilderComb, ISqlGroupBuilder
    {
        private readonly SqlBuildClause _clause;
        private readonly ISqlHavingBuilder _having;

        public SqlGroupBuilder(
            ISqlBuilder builder,
            SqlBuildClause group,
            SqlBuildClause having,
            SqlBuildClause order) : base(builder)
        {
            _having = new SqlHavingBuilder(builder, having, order);
            _clause = group;
        }

        /// <summary>
        ///     Creates a GROUP BY clause and adds an array of SQL expressions (each expression specifies
        ///     a column or a non-aggregate calculation on a column).
        /// </summary>
        /// <param name="groupExpressions">
        ///     An array of SQL expressions (each expression specifies a column or a non-aggregate calculation on a column).
        /// </param>
        /// <returns>
        ///     Returns the current ISqlGroupBuilder object.
        /// </returns>
        public ISqlGroupBuilder GroupBy(params string[] groupExpressions)
        {
            _clause.Add(new GroupElement(groupExpressions));

            return this;
        }

        /// <summary>
        ///     Creates a GROUP BY clause, and adds a raw SQL string containing one or more column expressions (each expression
        ///     specifies a column or a non-aggregate calculation on a column).
        /// </summary>
        /// <param name="groupClause">
        ///     A raw SQL string containing one or more column expressions (each expression specifies a column or a 
        ///     non-aggregate calculation on a column).
        /// </param>
        /// <returns>
        ///     Returns the current ISqlGroupBuilder object.
        /// </returns>
        public ISqlGroupBuilder GroupBy(string groupClause)
        {
            _clause.Add(new GroupElement(new string[] { groupClause }));

            return this;
        }

        /*public ISqlGroupBuilder GroupBy(string groupClause, string alias)
        {
            throw new NotImplementedException();
        }*/

        #region Having

        /// <summary>
        ///     Creates a HAVING clause, and adds a search condition to the HAVING clause. You can specify a SQL 
        ///     expression on the left of the operator and a SQL parameter on the right of the operator. The operator is '='.
        /// </summary>
        /// <param name="left">A SQL expression on the left of the operator.</param>
        /// <param name="parameter">
        ///     An ISqlParameter object which represents a SQL parameter on the right of the operator. 
        ///     It can be created by calling the SqlBuilder.Parameter method.
        /// </param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr Having(string left, ISqlParameter parameter)
        {
            return _having.Having(left, parameter);
        }

        /// <summary>
        ///     Creates a HAVING clause and adds a search condition to the HAVING clause. You can specify a SQL expression 
        ///     on the left of the operator and a SQL parameter on the right of the operator.
        /// </summary>
        /// <param name="left">A SQL expression on the left of the operator.</param>
        /// <param name="sqlOperator">
        ///     An enumeration value of SqlBinaryOperator, which is the operator to test the two expressions on the left and right.
        /// </param>
        /// <param name="parameter">
        ///     An ISqlParameter object which represents a SQL parameter on the right of the operator. 
        ///     It can be created by calling the SqlBuilder.Parameter method.
        /// </param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr Having(string left, SqlBinaryOperator sqlOperator, ISqlParameter parameter)
        {
            return _having.Having(left, sqlOperator, parameter);
        }

        /// <summary>
        ///     Creates a HAVING clause and adds a search condition to the HAVING clause. You can specify SQL expressions
        ///     on both the left and right of the operator. The operator is '='.
        /// </summary>
        /// <param name="left">A SQL expression on the left of the operator.</param>
        /// <param name="right">A SQL expression on the right of the operator.</param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr Having(string left, string right)
        {
            return _having.Having(left, right);
        }

        /// <summary>
        ///     Creates a HAVING clause and adds a search condition to the HAVING clause. You can specify SQL expressions 
        ///     on both the left and right of the operator.
        /// </summary>
        /// <param name="left">A SQL expression on the left of the operator.</param>
        /// <param name="sqlOperator">
        ///     An enumeration value of SqlBinaryOperator, which is the operator to test the two expressions on the left and right.
        /// </param>
        /// <param name="right">A SQL expression on the right of the operator.</param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr Having(string left, SqlBinaryOperator sqlOperator, string right)
        {
            return _having.Having(left, sqlOperator, right);
        }

        /// <summary>
        ///     Creates a HAVING clause and adds a search condition to the HAVING clause. You can specify a SQL expression on the 
        ///     left of the operator and a SQL subquery on the right side. The operator is '='.
        /// </summary>
        /// <param name="left">A SQL expression on the left of the operator.</param>
        /// <param name="sqlBuilder">An ISqlBuilder object which represents a SQL subquery on the right of the operator.</param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr Having(string left, ISqlBuilder sqlBuilder)
        {
            return _having.Having(left, sqlBuilder);
        }

        /// <summary>
        ///     Creates a HAVING clause and adds a search condition to the HAVING clause. You can specify a SQL expression on the 
        ///     left of the operator and a SQL subquery on the right side.
        /// </summary>
        /// <param name="left">A SQL expression on the left of the operator.</param>
        /// <param name="sqlOperator">
        ///     An enumeration value of SqlBinaryOperator which is the operator to test the two expressions on the left and right.
        /// </param>
        /// <param name="sqlBuilder">An ISqlBuilder object which represents a SQL subquery on the right of the operator.</param>
        /// <returns></returns>
        public ISqlHavingAndOr Having(string left, SqlBinaryOperator sqlOperator, ISqlBuilder sqlBuilder)
        {
            return _having.Having(left, sqlOperator, sqlBuilder);
        }

        /// <summary>
        ///     Creates a HAVING clause, and adds a search condition to the HAVING clause by an ISqlHavingCondition object.
        /// </summary>
        /// <param name="condition">
        ///     An ISqlHavingCondition object which represents a search condition in the HAVING clause.
        ///     This object can be created by using the SqlBuilder.Having method and so on.
        /// </param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr Having(ISqlHavingCondition condition)
        {
            return _having.Having(condition);
        }

        /// <summary>
        ///     Creates a HAVING clause, and adds a search condition to the HAVING clause by an ISqlHavingCondition 
        ///     object and specifies the name for the search condition.
        /// </summary>
        /// <param name="condition">
        ///     An ISqlHavingCondition object which represents a search condition in the HAVING clause.
        ///     This object can be created by using the SqlBuilder.Having method and so on.
        /// </param>
        /// <param name="havingName">The name for the search condition.</param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr Having(ISqlHavingCondition condition, string alias)
        {
            return _having.Having(condition, alias);
        }

        /// <summary>
        ///     Creates a HAVING clause and adds a raw SQL to the HAVING clause.
        /// </summary>
        /// <param name="havingClause">A string of raw SQL to be added to the HAVING clause.</param>
        /// <param name="parameters">
        ///     (Optional) An array of ISqlParameter objects which define the SQL parameters used in the raw SQL.
        /// </param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr HavingRaw(string havingClause, params ISqlParameter[] parameters)
        {
            return _having.HavingRaw(havingClause, parameters);
        }

        /// <summary>
        ///     Creates a HAVING clause, and adds a raw SQL to the HAVING clause and specifies a name for the raw SQL.
        /// </summary>
        /// <param name="havingClause">A string of raw SQL to be added to the HAVING clause.</param>
        /// <param name="alias">The name for the raw SQL.</param>
        /// <param name="parameters">
        ///     (Optional) An array of ISqlParameter objects which define the SQL parameters used in the raw SQL.
        /// </param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr HavingRaw(string havingClause, string alias, params ISqlParameter[] parameters)
        {
            return _having.HavingRaw(havingClause, alias, parameters);
        }

        /// <summary>
        ///     Creates a HAVING clause, and adds a search condition to the HAVING clause. You can specify 
        ///     a SQL expression on the left the operator and a specific value on the right of the operator. The operator is '='.
        /// </summary>
        /// <param name="left">An expression on the left of the operator.</param>
        /// <param name="value">
        ///     The specific value on the right of the operator.
        /// </param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr HavingValue(string left, object value)
        {
            return _having.HavingValue(left, value);
        }

        /// <summary>
        ///     Creates a HAVING clause, and adds a search condition to the HAVING clause. You can specify the SQL 
        ///     expression on the left the operator and a specific value on the right of the operator.
        /// </summary>
        /// <param name="left">An expression on the left of the operator.</param>
        /// <param name="sqlOperator">
        ///     An enumeration value of SqlBinaryOperator which is the operator to test the expression on the left and the value on the right.
        /// </param>
        /// <param name="value">
        ///     The specific value on the right of the operator.
        /// </param>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object which can be used to add more search conditions to the current HAVING clause.
        /// </returns>
        public ISqlHavingAndOr HavingValue(string left, SqlBinaryOperator sqlOperator, object value)
        {
            return _having.HavingValue(left, sqlOperator, value);
        }

        /// <summary>
        ///     Returns an ISqlHavingAndOr object that can be used to add a search condition to a group or an aggregate in the HAVING clause.
        /// </summary>
        /// <returns>
        ///     Returns the ISqlHavingAndOr object that can add a search condition to a group or an aggregate in the HAVING clause.
        /// </returns>
        public ISqlHavingAndOr AsHaving()
        {
            return (SqlHavingBuilder)_having;
        }
        #endregion

        #region OrderBy
        /// <summary>
        ///     Creates an ORDER BY clause and specifies one or more sort criteria in ascending order.
        /// </summary>
        /// <param name="orderExpressions">
        ///     (Optional) An array of expressions which specify a column or an expression on which to sort the query result set.
        /// </param>
        /// <returns>
        ///     Returns an ISqlOrderThenBuilder object which can be used to add more sort criteria to the current ORDER BY clause.
        /// </returns>
        public ISqlOrderThenBuilder OrderByAscending(params string[] orderExpressions)
        {
            return _having.OrderByAscending(orderExpressions);
        }

        /// <summary>
        ///     Creates an ORDER BY clause and specifies one or more sort criteria in descending order.
        /// </summary>
        /// <param name="orderExpressions">
        ///     (Optional) An array of expressions which specify a column or an expression on which to sort the query result set.
        /// </param>
        /// <returns>
        ///     Returns an ISqlOrderThenBuilder object which can be used to add more sort criteria to the current ORDER BY clause.
        /// </returns>
        public ISqlOrderThenBuilder OrderByDescending(params string[] orderExpressions)
        {
            return _having.OrderByDescending(orderExpressions);
        }

        /// <summary>
        ///     Creates an ORDER BY clause and specifies one or more sort criteria using a raw SQL.
        /// </summary>
        /// <param name="orderClause">A string of raw SQL which specifies one or more sort criteria.</param>
        /// <returns>
        ///     Returns an ISqlOrderThenBuilder object which can be used to add more sort criteria to the current ORDER BY clause.
        /// </returns>
        public ISqlOrderThenBuilder OrderByRaw(string orderClause)
        {
            return _having.OrderByRaw(orderClause);
        }

        /// <summary>
        ///     Creates an ORDER BY clause; and specifies one or more sort criteria using a raw SQL and specifies an alias for the raw SQL.
        /// </summary>
        /// <param name="orderClause">A string of raw SQL which specifies one or more sort criteria.</param>
        /// <param name="alias">The alias for the raw SQL.</param>
        /// <returns>
        ///     Returns an ISqlOrderThenBuilder object which can be used to add more sort criteria to the current ORDER BY clause.
        /// </returns>
        public ISqlOrderThenBuilder OrderByRaw(string orderClause, string alias)
        {
            return _having.OrderByRaw(orderClause, alias);
        }

        /// <summary>
        ///     Returns an ISqlOrderThenBuilder object which can be used to add sorting criteria to the ORDER clause.
        /// </summary>
        /// <returns>
        ///     Returns an ISqlOrderThenBuilder object which can be used to add sorting criteria to the ORDER clause.
        /// </returns>
        public ISqlOrderThenBuilder AsOrderBy()
        {
            return _having.AsOrderBy();
        }
        #endregion
    }
}
